package com.zenika.zentracker.hibernate.listener;

import java.io.Serializable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.engine.EntityEntry;
import org.hibernate.event.FlushEntityEvent;
import org.hibernate.event.def.DefaultFlushEntityEventListener;
import org.hibernate.persister.entity.EntityPersister;

import com.zenika.zentracker.hibernate.info.UpdatedObjectInfo;
import com.zenika.zentracker.hibernate.interceptor.SessionStatisticsInterceptor;

/**
 * Custom Listener used to retrieve the dirty properties.
 * 
 * In the event listeners of Hibernate the <tt>DefaultFlushEntityEventListener</tt> is the place where the dirty
 * properties are calculated.<br/>
 * 
 * Instead of doing again the same job, this listener can be used, and defined to take place <b>AFTER</b> the default
 * listener.<br/>
 * 
 * This object logs all the dirty objects, and for every dirty object the modified property with the old and new value.
 * 
 * Configuration Sample in the hibernate.cfg.xml file:
 * 
 * <listener type="flush-entity" class="com.zenika.zentracker.hibernate.listener.EntityCheckDirtyListener"/>
 * 
 * @author Zenika
 * 
 */
public class EntityCheckDirtyListener extends DefaultFlushEntityEventListener {

    private static final long serialVersionUID = 3832219603961288458L;
    private static final Log log = LogFactory.getLog("com.zenika.ZenTracker");

    /*
     * (non-Javadoc)
     * 
     * @see org.hibernate.event.FlushEntityEventListener#onFlushEntity(org.hibernate.event.FlushEntityEvent)
     */
    public void onFlushEntity(FlushEntityEvent event) throws HibernateException {
        // Call the super method to trigger the calculation of the dirty properties.
        super.onFlushEntity(event);
        
        // Collection information from the Event
        final Object entity = event.getEntity();
        final Object[] values = event.getPropertyValues();
        final EntityEntry entry = event.getEntityEntry();
        int[] dirtyProperties = event.getDirtyProperties();

        // Collect data from the EntityEntry
        final Serializable id = entry.getId();
        final Object[] loadedState = entry.getLoadedState();
        final EntityPersister persister = entry.getPersister();

        // Collect data from the EntityPersister
        final String[] propertyNames = persister.getPropertyNames();

        // Logging of the Dirty Properties
        if (dirtyProperties != null) {
            for (int i = 0; i < dirtyProperties.length; i++) {
                int dirtyIndex = dirtyProperties[i];
                                                 
                UpdatedObjectInfo objectInfo = new UpdatedObjectInfo(entity, id);
                objectInfo.setPropertyName(propertyNames[dirtyIndex]);
                objectInfo.setPreviousValue(loadedState[dirtyIndex].toString());
                objectInfo.setNewValue(values[dirtyIndex].toString());

                registerObjectInfo(event, objectInfo);
            }
        }
    }
    
    private void registerObjectInfo(FlushEntityEvent event, UpdatedObjectInfo objectInfo) {
        Interceptor interceptor = event.getSession().getInterceptor();
        
        if (SessionStatisticsInterceptor.class.isAssignableFrom(interceptor.getClass())) {
            ((SessionStatisticsInterceptor) interceptor).addUpdatedObjectInfo(objectInfo);
        }
    }
}
